package auction.chapter12;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.hibernate.CallbackException;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Session;
import org.hibernate.type.Type;

import auction.chapter12.model.AuditLogRecord;

public class AuditLogInterceptor extends EmptyInterceptor {
	private Session session;
	private Long userId;
	
	private Set inserts=new HashSet();
	private Set updates=new HashSet();
	
	public void setSession(Session s){
		this.session=s;
	}
	
	public void setUserId(Long uid){
		this.userId=uid;
	}
	
	/**
	 * Called before an object is saved. The interceptor may modify the state, which will be used for the SQL INSERT and propagated to the persistent object. 
	 * 
	 * entity: the instance
	 * id: PK
	 * state: values for each property
	 * propertyNames: names of each property
	 * types: org.hibernate.type of each property
	 * 
	 * @return true if the user modified the state in any way.
	 */
	public boolean onSave(Object entity,Serializable id,Object[] state,String[] propertyNames,Type[] types)
					throws CallbackException{
	    out("onSave:");
	    out(" Object entity:"+entity);
	    out(" Serializable id:"+id);
	    for(int i=0;i<state.length;i++){
	        out(" Object[] state:"+state[i]);
	    }
	    for(int i=0;i<propertyNames.length;i++){
            out(" String[] propertyNames:"+propertyNames[i]);
        }
        for(int i=0;i<types.length;i++){
            out(" Type[] types:"+types[i]);
        }
	    
		if(entity instanceof Auditable){
			inserts.add(entity);
			
			/* seems it's can be saved */
			//AuditLogRecord domain=new AuditLogRecord("direct onSave",999L,entity.getClass(),userId);
	        //session.save(domain);
	        
	        /* cause Message2 flushed but PK is null */
	        //session.getTransaction().commit();
	        //session.close();
		}
		
		/*  //infinite loop, when save AuditLogRecord cause onSave() invoked
		AuditLogRecord domain=new AuditLogRecord("direct onSave",999L,entity.getClass(),userId);
        session.save(domain);
		*/
		return true;
	}
	
	/**
	 * Called when an object is detected to be dirty, during a flush. 
	 * 
     * entity: the instance
     * id: PK
     * currentState: updated values of each property
     * previousState: old values of each property
     * propertyNames: names of each property
     * types: org.hibernate.type of each property
     * 
     * @return true if the user modified the currentState in any way.
	 */
	public boolean onFlushDirty(Object entity,Serializable id,Object[] currentState,Object[] previousState,
			String[] propertyNames,Type[] types)throws CallbackException{
	    out("onFlushDirty:");
	    out(" Object entity:"+entity);
	    out(" Serializable id:"+id);
	    for(int i=0;i<currentState.length;i++){
	        out(" Object[] currentState:"+currentState[i]);
	    }
	    for(int i=0;i<previousState.length;i++){
	        out(" Object[] previousState:"+previousState[i]);
	    }
	    for(int i=0;i<propertyNames.length;i++){
	        out(" String[] propertyNames:"+propertyNames[i]);
	    }
	    for(int i=0;i<types.length;i++){
	        out(" Type[] types:"+types[i]);
	    }
        
		if(entity instanceof Auditable){
			updates.add(entity);
		}
		return false;
	}
	
	/**
	 * Called after a flush that actually ends in execution of the SQL statements required to synchronize in-memory state with the database.
	 * 
	 *  @param iterator the iterator of entity
	 */
	public void postFlush(Iterator iterator)throws CallbackException{
	    
	    out("postFlush:");
	    for(;iterator.hasNext();){
	        //the instance that will be stored. such as Message2
	        Object obj=iterator.next();
	        out(" obj:"+obj);
	    }
	    
		try{
			for(Iterator it=inserts.iterator();it.hasNext();){
				Auditable entity=(Auditable)it.next();
				AuditLog.logEvent("create",entity,userId,session.connection());
			}
			
			for(Iterator it=updates.iterator();it.hasNext();){
				Auditable entity=(Auditable)it.next();
				AuditLog.logEvent("update",entity,userId,session.connection());
			}
		}finally{
			inserts.clear();
			updates.clear();
		}
	}
	
	private void out(Object obj)
    {
        System.out.println("--->" + obj);
    }
}
